﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml.Serialization;
using ImdbServices;
using System.Xml;

namespace Catalogit
{
    public partial class MainForm : Form
    {
        #region Private Member Variables

        static ImdbServices.Imdb imdb = new ImdbServices.Imdb();
        Movie currentMovie = new Movie();
        private string MOVIE_PATH = string.Empty;
        private string VLC_PATH = string.Empty;
        private const string METADATA_FILE_NAME = "\\metadata.xml";

        #endregion

        #region Constructors
        
        public MainForm()
        {
            InitializeComponent();
            IntializeEvents();
            LoadSettings();
            gvMovies.AutoGenerateColumns = false;
            BindMovieGrid();
            
        }

        #endregion

        #region Events
        
        // DataBound Controls
        private void dataGridView1_MouseUp(object sender, MouseEventArgs e)
        {
            // Load context menu on right mouse click
            DataGridView.HitTestInfo hitTestInfo;
            if (e.Button == MouseButtons.Right)
            {
                hitTestInfo = gvMovies.HitTest(e.X, e.Y);
                if (hitTestInfo.Type == DataGridViewHitTestType.Cell && hitTestInfo.ColumnIndex == 0)
                {
                    gvMovies.ClearSelection();
                    gvMovies.Rows[hitTestInfo.RowIndex].Cells[0].Selected = true;
                    contextMenuStrip1.Show(gvMovies, new Point(e.X, e.Y));
                }
            }
        }

        private void tvImdbSearchResults_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (tvImdbSearchResults.SelectedNode != null)
            {
                ShowWait(true,"Downloading Movie Details");
                imdb.GetMovieInfoAsync(Convert.ToInt64(e.Node.Tag), false);
            }
        }

        
        //Menus
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void contextMenuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            Int32 selectedCellCount = gvMovies.GetCellCount(DataGridViewElementStates.Selected);

            if (selectedCellCount > 0)
            {
                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < selectedCellCount; i++)
                {
                    sb.Append(gvMovies.SelectedCells[i].Value);
                }

                

                string vlcMoviePath = string.Format("\"{0}\\{1}\"",MOVIE_PATH, sb.ToString());
                string actualMoviePath = Path.Combine(MOVIE_PATH, sb.ToString());

                lblMoviePath.Text = sb.ToString();

                if (e.ClickedItem.Text == "Search IMDB")
                {
                        ShowWait(true,"Searching IMDB");
                        ResetSearchResults();
                        SearchImdbMovie(sb.ToString());
                        searchTabs.SelectedIndex = 0; //select imdb search

                }
                else if(e.ClickedItem.Text == "Edit Details")
                {
                    ResetSearchResults();
                    EditMovieDetails(true,actualMoviePath);
                }
                else if(e.ClickedItem.Text == "Play Movie")
                {
                    
                    string vlcPath = string.Format(VLC_PATH +  "\\vlc.exe");
                    System.Diagnostics.Process.Start(vlcPath, vlcMoviePath);
                    
                }
            }

        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LoadSettingsForm();
            
        }
        
        //Buttons
        private void btnSaveMetaData_Click(object sender, EventArgs e)
        {
            ShowWait(true,"Saving Movie Data");
            string actualMoviePath = Path.Combine(MOVIE_PATH, lblMoviePath.Text);
            SerializeMovie(actualMoviePath + "\\metadata.xml", currentMovie);
            
            if(chkSaveFolderImage.Checked)
            {
                pbImdbImage.Image.Save(actualMoviePath + "\\folder.jpg");
            }
            
            if(chkRenameFolder.Checked)
            {
                Random random = new Random();
                
                string tmpFolder = "movietmp" + random.Next(1,500).ToString();

                string titleFix = txtTitle.Text;
                titleFix = titleFix.Replace(":", " - ");
                titleFix = titleFix.Replace("?", "");
                
                Directory.Move(Path.Combine(MOVIE_PATH,lblMoviePath.Text), Path.Combine(MOVIE_PATH,tmpFolder));
                Directory.Move(Path.Combine(MOVIE_PATH,tmpFolder),Path.Combine(MOVIE_PATH,titleFix));
                
                
            }

            BindMovieGrid();
            ShowWait(false,string.Empty);

        }

        private void btnLocalSearch_Click(object sender, EventArgs e)
        {

            BindMovieGrid();

            foreach (DataGridViewRow row in gvMovies.Rows)
            {

                if (!row.Cells[0].Value.ToString().ToLower().Contains(txtLocalSearchTitle.Text))
                {
                    gvMovies.CurrentCell = null;
                    row.Visible = false;
                }

                if (!row.Cells[1].Value.ToString().ToLower().Contains(txtLocalSearchGenre.Text))
                {
                    gvMovies.CurrentCell = null;
                    row.Visible = false;
                }
            }
        }

        private void lnkReset_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            ResetLocalSearch();
            BindMovieGrid();
        }

        private void lnkRefreshMovies_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            BindMovieGrid();
        }


        
        //Movie Events
        private void MovieInfoDownloadedEventHandler(Movie movie)
        {
            currentMovie = movie;
            EditMovieDetails(false,null);
            ShowWait(false,string.Empty);
        }

        private void SearchResultsDownloaded(MoviesResultset movie)
        {
            ShowWait(false,string.Empty);

            tvImdbSearchResults.Nodes.Clear();

            TreeNode movieNode;

            if (movie.PopularTitles.Count > 0)
            {
                movieNode = new TreeNode("Popular Titles");
                foreach (Movie Movie in movie.PopularTitles)
                {
                    movieNode.Nodes.Add(new TreeNode
                                            {
                        Text = Movie.Title,
                        Tag = Movie.ImdbID
                    });

                    movieNode.Expand();
                }
                tvImdbSearchResults.Nodes.Add(movieNode);
            }

            if (movie.PartialMatches.Count > 0)
            {
                movieNode = new TreeNode("Partial Matches");
                foreach (Movie Movie in movie.PartialMatches)
                {
                    movieNode.Nodes.Add(new TreeNode
                                            {
                        Text = Movie.Title,
                        Tag = Movie.ImdbID
                    });
                }
                tvImdbSearchResults.Nodes.Add(movieNode);
            }

            if (movie.ExactMatches.Count > 0)
            {
                movieNode = new TreeNode("Exact Matches");
                foreach (Movie Movie in movie.ExactMatches)
                {

                    movieNode.Nodes.Add(new TreeNode
                                            {
                        Text = Movie.Title,
                        Tag = Movie.ImdbID
                    });
                }
                tvImdbSearchResults.Nodes.Add(movieNode);
            }
        }

        #endregion

        #region Methods
        
        private List<Movie> GetMovies()
        {
            if(!Directory.Exists(MOVIE_PATH))
            {
                MessageBox.Show("Invalid Movie Path");
                return null;
            }

            List<Movie> movies = new List<Movie>();

            DirectoryInfo di = new DirectoryInfo(MOVIE_PATH);

            foreach (DirectoryInfo info in di.GetDirectories())
            {
                Movie movieDetails = new Movie();
                if(File.Exists(info.FullName + METADATA_FILE_NAME))
                    movieDetails = DeserliazeMovie(info.FullName + METADATA_FILE_NAME);
                movieDetails.Title = info.Name;

                StringBuilder sbGenres = new StringBuilder();
                foreach (string genre in movieDetails.Genres)
                {
                    sbGenres.AppendFormat("{0},",genre);
                }

                //borrowed TrailerURL property to set Genres as string as I did not want to edit the IMDB service Movie Class
                movieDetails.TrailerURL = sbGenres.ToString().TrimEnd(','); 
                movies.Add(movieDetails);
            }

            return movies;
        
        }

        private void SearchImdbMovie(string title)
        {
            imdb.SearchMovieAsync(title, false, true );
        }
        
        private Movie DeserliazeMovie(string filename)
        {
            // Create an instance of the XmlSerializer specifying type and namespace.
            XmlSerializer serializer = new XmlSerializer(typeof(Movie));

            // A FileStream is needed to read the XML document.
            FileStream fs = new FileStream(filename, FileMode.Open,FileAccess.Read);
            XmlReader reader = new XmlTextReader(fs);

            // Declare an object variable of the type to be deserialized.
            // Use the Deserialize method to restore the object's state.
            Movie movie  = (Movie)serializer.Deserialize(reader);

            return movie;
        }

        private void SerializeMovie(string filename, Movie movieObject)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Movie));
            Movie movie = movieObject;

            /* Create a StreamWriter to write with. First create a FileStream
             object, and create the StreamWriter specifying an Encoding to use. */
            FileStream fs = new FileStream(filename, FileMode.Create);
            TextWriter writer = new StreamWriter(fs, new UTF8Encoding());

            // Serialize using the XmlTextWriter.
            serializer.Serialize(writer, movie);
            writer.Close();
        }

        private void LoadSettings()
        {
            if (!string.IsNullOrEmpty(Properties.Settings.Default.MoviePath) && !string.IsNullOrEmpty(Properties.Settings.Default.VLCPath))
            {
                MOVIE_PATH = Properties.Settings.Default.MoviePath;
                VLC_PATH = Properties.Settings.Default.VLCPath;
            }
            else
            {
                LoadSettingsForm();
            }
        }

        private void LoadSettingsForm()
        {
            SettingsForm sf = new SettingsForm();
            sf.ShowDialog(this);
        }

        private void EditMovieDetails(bool useLocal, string actualMoviePath)
        {
            //Decalre a movie variable to load from
            Movie movie;

            if (!useLocal)
            {
                //If not in local mode then load from the imdb search results
                movie = currentMovie;

                if (movie.ImageURL != "")
                {
                    pbImdbImage.Image = Image.FromStream(new MemoryStream(new System.Net.WebClient().DownloadData(movie.ImageURL)));
                    pbImdbImage.Show();
                }    
            }
            else
            {
                //If local mode deserialize from the metadata exml

                string metaDataPath = actualMoviePath + "\\metadata.xml";
                string imagePath = actualMoviePath + "\\folder.jpg";


                if (!File.Exists(metaDataPath))
                {
                    MessageBox.Show("Missing MetaData File", "Missing MetaData", MessageBoxButtons.OK,MessageBoxIcon.Information);
                    return;
                }

                try
                {
                    movie = DeserliazeMovie(metaDataPath);
                }
                catch (InvalidOperationException)
                {
                    MessageBox.Show("Invalid XML in metadata.xml", "Invalid XML", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }
                

                if (File.Exists(imagePath))
                {
                    pbImdbImage.Image = Image.FromFile(imagePath);
                    pbImdbImage.Show();
                }
            }
            
            txtTitle.Text = movie.Title;
            txtYear.Text = movie.Year.ToString();
            txtRating.Text = movie.Rating.ToString();
            txtPlot.Text = movie.Description;

            StringBuilder sbStudios = new StringBuilder();

            foreach (string studio in movie.Studios)
            {
                sbStudios.AppendFormat("{0},", studio);
            }

            txtStudio.Text = sbStudios.ToString().TrimEnd(',');

            StringBuilder sbGenres = new StringBuilder();

            foreach (string item in movie.Genres)
            {
                sbGenres.AppendFormat("{0},", item);
            }

            txtGenres.Text = sbGenres.ToString().TrimEnd(',');

            StringBuilder sbActors = new StringBuilder();

            foreach (Person person in movie.Actors)
            {
                sbActors.AppendFormat("{0},", person.Name);
            }

            txtActors.Text = sbActors.ToString().TrimEnd(',');
        }

        private void IntializeEvents()
        {
            gvMovies.MouseUp += dataGridView1_MouseUp;
            contextMenuStrip1.ItemClicked += contextMenuStrip1_ItemClicked;
            exitToolStripMenuItem.Click += exitToolStripMenuItem_Click;
            imdb.SearchResultsDownloaded += SearchResultsDownloaded;
            imdb.MovieInfoDownloaded += MovieInfoDownloadedEventHandler;
            tvImdbSearchResults.AfterSelect += tvImdbSearchResults_AfterSelect;
        }

        private void BindMovieGrid()
        {
            gvMovies.DataSource = GetMovies();
        }

        private void ResetLocalSearch()
        {
            txtLocalSearchTitle.Text = string.Empty;
            txtLocalSearchGenre.Text = string.Empty;
        }

        private void ResetSearchResults()
        {
            txtTitle.Text = string.Empty;
            txtGenres.Text = string.Empty;
            txtActors.Text = string.Empty;
            txtYear.Text = string.Empty;
            txtRating.Text = string.Empty;
            txtStudio.Text = string.Empty;
            txtPlot.Text = string.Empty;
            pbImdbImage.Hide();
        }

        private void ShowWait(bool isShowing,string showWaitText)
        {
            progressBar1.Style = ProgressBarStyle.Marquee;
            progressBar1.MarqueeAnimationSpeed = 20;
            
            progressBar1.Visible = isShowing;
            lblShowWaitText.Visible = isShowing;
            lblShowWaitText.Text = string.Format("{0}...", showWaitText);
        }


        #endregion

    }
   
}
